package com.mjk.common.util;

import com.mjk.common.tools.logger.BizLogger;
import lombok.Data;

import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 身份证工具类
 * @author jafek
 */
public class IDCardUtils {
    /**
     * 身份证号码验证 1、号码的结构 公民身份号码是特征组合码，由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为：六位数字地址码，
     * 八位数字出生日期码，三位数字顺序码和一位数字校验码。 2、地址码(前六位数）
     * 表示编码对象常住户口所在县(市、旗、区)的行政区划代码，按GB/T2260的规定执行。 3、出生日期码（第七位至十四位）
     * 表示编码对象出生的年、月、日，按GB/T7408的规定执行，年、月、日代码之间不用分隔符。 4、顺序码（第十五位至十七位）
     * 表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号， 顺序码的奇数分配给男性，偶数分配给女性。 5、校验码（第十八位数）
     * （1）十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, ... , 16 ，先对前17位数字的权求和
     * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 
     * （2）计算模 Y = mod(S, 11) （3）通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0 X 9 8 7 6 5 4 3 2
     */
	public static CardInfo getCardInfo(String idNumber){
		CardInfo info = new CardInfo();		
        String[] valCodeArr = { "1", "0", "x", "9", "8", "7", "6", "5", "4","3", "2" };
        String[] wi = { "7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7","9", "10", "5", "8", "4", "2" };
        String ai = "";
        // ================ 号码的长度 15位或18位 ================
        if (idNumber.length() != 15 && idNumber.length() != 18) {
        	info.setErrInfo("身份证号码长度应该为15位或18位。");            
            return info;
        }
        // =======================(end)========================
        // ================ 数字 除最后以为都为数字 ================
        if (idNumber.length() == 18) {
            ai = idNumber.substring(0, 17);
        } else {
            ai = idNumber.substring(0, 6) + "19" + idNumber.substring(6, 15);
        }
        if (!StrUtils.isNumeric(ai)) {
        	info.setErrInfo("身份证15位号码都应为数字 ; 18位号码除最后一位外，都应为数字。");               
            return info;
        }
        // =======================(end)========================

        // ================ 出生年月是否有效 ================
        String strYear = ai.substring(6, 10);// 年份
        String strMonth = ai.substring(10, 12);// 月份
        String strDay = ai.substring(12, 14);// 月份
        if (!TimeUtils.isDate(strYear + "-" + strMonth + "-" + strDay)) {
        	info.setErrInfo("身份证生日无效。");            
            return info;
        }
        GregorianCalendar gc = new GregorianCalendar();
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
        try {
            if ((gc.get(Calendar.YEAR) - Integer.parseInt(strYear)) > 150
                    || (gc.getTime().getTime() - s.parse(
                            strYear + "-" + strMonth + "-" + strDay).getTime()) < 0) {
            	info.setErrInfo("身份证生日不在有效范围。");            
                return info;
            }
        } catch (Exception e) {
            BizLogger.error(e);
        }
        if (Integer.parseInt(strMonth) > 12 || Integer.parseInt(strMonth) == 0) {
        	info.setErrInfo("身份证月份无效");            
            return info;
        }
        if (Integer.parseInt(strDay) > 31 || Integer.parseInt(strDay) == 0) {
        	info.setErrInfo("身份证日期无效");            
            return info;
        }
        info.setBrithDay(strYear + "-" + strMonth + "-" + strDay);
        info.setAge(TimeUtils.getAge(strYear + "-" + strMonth + "-" + strDay)+"");
        // =====================(end)=====================

        // ================ 地区码时候有效 ================        
        if (ObjectUtils.isEmpty(NativePlace.getArea(ai.substring(0, 6)))){
        	info.setErrInfo("身份证地区编码错误。");            
            return info;
        }
        info.setProvinceCode(ai.substring(0, 2));
        info.setProvince(NativePlace.getArea(ai.substring(0, 2)));
        info.setCityCode(ai.substring(0, 4));
        info.setCity(NativePlace.getArea(ai.substring(0, 4)));
        info.setCountryCode(ai.substring(0, 6));
        info.setCounty(NativePlace.getArea(ai.substring(0, 6)));
        // ==============================================
        //  =================第17位 单数位男 双数位女
        String sex = ai.substring(16, 17);
        if(Integer.parseInt(sex)%2 == 0){//女
        	info.setSex("2");
        }else{
        	info.setSex("1");//男
        }
        // ================ 判断最后一位的值 ================
        int totalmulAiWi = 0;
        for (int i = 0; i < 17; i++) {
        	totalmulAiWi = totalmulAiWi
                    + Integer.parseInt(String.valueOf(ai.charAt(i)))
                    * Integer.parseInt(wi[i]);
        }
        int modValue = totalmulAiWi % 11;
        String strVerifyCode = valCodeArr[modValue].toUpperCase();
        ai = ai + strVerifyCode;

        if (idNumber.length() == 18 &&!ai.equals(idNumber)) {
            info.setErrInfo("身份证无效，不是合法的身份证号码");
            return info;
        }        
		return info;
	}
    /**
     * 功能：身份证的有效验证
     * 
     * @param idNumber
     *            身份证号
     * @return 有效：返回"" 无效：返回String信息
     */
    public static String isValidCard(String idNumber) {
        return getCardInfo(idNumber).getErrInfo();
    }
    /**
     * 解析地址
     */
    public static Address addressResolution(String address){
        String shengregex= """
                ((?<province>[^省]+省|.+自治区)|上海|北京|天津|重庆)*(?<city>[^市]+市|.+自治州)*(?<county>[^县]+县|.+区|.+镇|.+局)? (?<town>[^区]+区|.+镇)?(?<village>.*)
                """;
        Matcher m=Pattern.compile(shengregex).matcher(address);
        Address add = new Address();
        while(m.find()){
            if(ObjectUtils.isEmpty(add.getProvince()))
                add.setProvince(m.group("province")==null?"":m.group("province").trim());
            if(ObjectUtils.isEmpty(add.getCity()))
                add.setCity(m.group("city")==null?"":m.group("city").trim());
            if(ObjectUtils.isEmpty(add.getCounty()))
                add.setCounty(m.group("county")==null?"":m.group("county").trim());
            if(ObjectUtils.isEmpty(add.getTown()))
                add.setTown(m.group("town")==null?"":m.group("town").trim());
            if(ObjectUtils.isEmpty(add.getVillage()))
                add.setVillage(m.group("village")==null?"":m.group("village").trim());
        }
        return add;
    }
    /*********************************** 身份证验证结束 ****************************************/
    /**
     * 身份证信息
     * @author mjk
     *
     */
    @Data
    public static class CardInfo{
        public CardInfo(){
            this.errInfo="";//默认为空
        }
    	private String errInfo;//错误信息
    	private String brithDay;//生日
    	private String age;//年龄
    	private String sex;//性别
    	private String province;//省
    	private String provinceCode;//省代码
    	private String city;//省    
    	private String cityCode;
    	private String county;//县 /区
    	private String countryCode;
    	
    	public String toAdress(){
    		return this.province+this.city+this.county;
    	}
    }

    /**
     * 地址信息
     */
    @Data
    public static class Address{
    	private String province;//省
    	private String city;//省
    	private String county;//县 /区
    	private String town;//镇
    	private String village;//村
    	//判断是否是有效地址
    	public boolean isValidate(){
            return !ObjectUtils.isEmpty(this.province) || !ObjectUtils.isEmpty(this.city);
        }
    }

}
